package cn.jingyuan.bee.utils.io.resource;

import cn.jingyuan.bee.utils.ClassUtils;
import cn.jingyuan.bee.utils.ObjectUtils;
import cn.jingyuan.bee.utils.StringUtils;
import cn.jingyuan.bee.utils.URLUtils;
import cn.jingyuan.bee.utils.io.FileUtils;
import cn.jingyuan.bee.utils.lang.Assert;

/**
 * ClassPath 单一资源访问类<br>
 * 传入路径 path 必须为相对路径，如果传入绝对路径，Linux 路径会去掉开头的“/”，而 Windows 路径会直接报错<br>
 * 传入的 path 所指向的资源必须存在，否则报错
 */
public class ClassPathResource extends UrlResource {

    private final String path;

    private final ClassLoader classLoader;

    private final Class<?> clazz;

    /**
     * 构造
     *
     * @param path 相对于 ClassPath 的路径
     */
    public ClassPathResource(String path) {
        this(path, null, null);
    }

    /**
     * 构造
     *
     * @param path 相对于 ClassPath 的路径
     * @param classLoader {@link ClassLoader}
     */
    public ClassPathResource(String path, ClassLoader classLoader) {
        this(path, classLoader, null);
    }

    /**
     * 构造
     *
     * @param path 相对于给定 Class 的路径
     * @param clazz {@link Class} 用于定位路径
     */
    public ClassPathResource(String path, Class<?> clazz) {
        this(path, null, clazz);
    }

    /**
     * 构造
     *
     * @param pathBaseClassLoader 相对路径
     * @param classLoader {@link ClassLoader}
     * @param clazz {@link Class} 用于定位路径
     */
    public ClassPathResource(String pathBaseClassLoader, ClassLoader classLoader, Class<?> clazz) {
        super(null);

        Assert.notNull(pathBaseClassLoader, "Path must not be null");

        final String normalizePath = normalizePath(pathBaseClassLoader);

        this.path = normalizePath;
        this.name = StringUtils.isBlank(normalizePath) ? null : FileUtils.getName(normalizePath);
        this.classLoader = ObjectUtils.defaultIfNull(classLoader, ClassUtils.getClassLoader());
        this.clazz = clazz;

        initUrl();
    }

    /**
     * 获得 Path
     *
     * @return path
     */
    public final String getPath() {
        return this.path;
    }

    /**
     * 获得绝对路径 Path<br>
     * 对于不存在的资源，返回拼接后的绝对路径
     *
     * @return 绝对路径 path
     */
    public final String getAbsolutePath() {
        if (FileUtils.isAbsolutePath(this.path)) {
            return this.path;
        }
        // url 在初始化的时候已经断言，此处始终不为 null
        return FileUtils.normalize(URLUtils.getDecodedPath(this.url));
    }

    /**
     * 获得 {@link ClassLoader}
     *
     * @return {@link ClassLoader}
     */
    public final ClassLoader getClassLoader() {
        return this.classLoader;
    }

    /**
     * 根据给定资源初始化 URL
     */
    private void initUrl() {
        if (null != this.clazz) {
            super.url = this.clazz.getResource(this.path);
        } else if (null != this.classLoader) {
            super.url = this.classLoader.getResource(this.path);
        } else {
            super.url = ClassLoader.getSystemResource(this.path);
        }
        if (null == super.url) {
            throw new NoResourceException("Resource of path [{}] not exist!", this.path);
        }
    }

    @Override
    public String toString() {
        return (null == this.path) ? super.toString() : "classpath:" + this.path;
    }

    /**
     * 标准化 Path 格式
     *
     * @param path Path
     *
     * @return 标准化后的 path
     */
    private String normalizePath(String path) {
        // 标准化路径
        path = FileUtils.normalize(path);
        path = StringUtils.removePrefix(path, StringUtils.SLASH);

        Assert.isFalse(FileUtils.isAbsolutePath(path), "Path [{}] must be a relative path !", path);
        return path;
    }

}
